
package net.sourceforge.jasonide.wizards;

import jason.jeditplugin.Config;
import jason.mas2j.*;

import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sourceforge.jasonide.Activator;
import net.sourceforge.jasonide.editors.*;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWizard;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;

/**
 * Wizard for create a new agent with default contents.
 * 
 * @author Daniel Nicholas Kiss
 */
public class NewModuleWizard extends Wizard implements INewWizard {
	private NewModuleWizardPage page;
	private ISelection selection;
	
	/**
	 * Constructor for NewFileAgentWizard.
	 */
	public NewModuleWizard() {
		super();
		setNeedsProgressMonitor(true);
	}
	
	/**
	 * Adding the page to the wizard.
	 */
	public void addPages() {
		page = new NewModuleWizardPage(selection);
		addPage(page);
	}
	
	/**
	 * This method is called when 'Finish' button is pressed in the wizard. We will create an operation and run it using
	 * wizard as execution context.
	 */
	public boolean performFinish() {
		try {
			doFinish(page.getContainerName(), page.getModuleName(), page.getAgentSource(), page.getModuleSource());
		} catch (CoreException e) {
			MessageDialog.openError(getShell(), "Error", e.getMessage());
		}
		return true;
	}
	
	/**
	 * The worker method. It will find the container, create the file if missing or just replace its contents, and open
	 * the editor on the newly created file.
	 */
	private void doFinish(String containerName, String moduleName, String agentSource, String moduleSource) throws CoreException {
		
		// create a sample file
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		
		IResource containerResource = root.findMember(new Path(containerName));
		if (!containerResource.exists() || !(containerResource instanceof IContainer)) {
			throwCoreException("Source folder \"" + containerName + "\" does not exist.");
		}
		IContainer container = (IContainer)containerResource;
		
		IFile agentFile = null;
		if (!agentSource.isEmpty()) {
			agentFile = root.getFile(new Path(agentSource));
			try {
				try {
					if (!agentFile.exists()) {
						InputStream stream = NewAgentWizard.openContentStream(container, agentSource, getShell());
						agentFile.create(stream, true, null);
						stream.close();
						NewAgentWizard.registerNewAgentInMas2JProjectFile(containerResource, agentFile, getShell());
					}
				} catch (IOException e) {
					MessageDialog.openError(getShell(), "Error", e.toString());
				}
				
				final IFile fAgentFile = agentFile;
				getShell().getDisplay().asyncExec(new Runnable() {
					public void run() {
						IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
						try {
							IDE.openEditor(page, fAgentFile, true);
						} catch (PartInitException e) {
							MessageDialog.openError(getShell(), "Error", e.toString());
						}
					}
				});
			} catch (Exception e) {
				agentFile = null;
				MessageDialog.openError(getShell(), "Error", e.toString());
			}
		}
		
		final IFile moduleFile;
		
		if (moduleSource.isEmpty()) {
			moduleFile = container.getFile(new Path(moduleName + "." + MAS2JProject.ASM_EXT));
		} else {
			if(moduleSource.indexOf('.') == -1) {
				moduleSource += '.' + MAS2JProject.ASM_EXT;
			}
			Path moduleSourcePath = new Path(moduleSource);
			if (moduleSourcePath.isAbsolute()) {
				
				String localPath;
				String dir;
				try {
					localPath = moduleSourcePath.toFile().getCanonicalPath();
					dir = container.getProject().getLocation().toFile().getParentFile().getCanonicalPath();
				} catch (IOException e) {
					localPath = moduleSourcePath.toFile().getAbsolutePath();
					dir = container.getProject().getLocation().toFile().getParentFile().getAbsolutePath();
				}
				
				int dirPos = localPath.toLowerCase().indexOf(dir.toLowerCase());
				if (dirPos != -1) localPath = localPath.substring(dirPos + dir.length());
				
				localPath.replace(java.io.File.separatorChar, '/');
				
				moduleFile = root.getFile(new Path(localPath));
			} else {
				moduleFile = container.getFile(moduleSourcePath);
			}
			
			if (moduleName.isEmpty()) {
				moduleName = moduleSourcePath.lastSegment().replaceFirst("\\.[^.]*$", "");
			}
		}
		
		try {
			if (!moduleFile.exists()) {
				InputStream stream = openContentStream(container, moduleName, agentSource);
				moduleFile.create(stream, true, null);
				stream.close();
			}
			if (agentFile != null) registerNewModuleInAgent(moduleName, agentFile, moduleFile, getShell());
		} catch (IOException e) {
			MessageDialog.openError(getShell(), "Error", e.toString());
		}
		
		getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
				try {
					IDE.openEditor(page, moduleFile, true);
				} catch (PartInitException e) {
					MessageDialog.openError(getShell(), "Error", e.toString());
				}
			}
		});
	}
	
	private void registerNewModuleInAgent(String moduleName, IFile agentFile, IFile moduleFile, Shell shell) throws CoreException {
		IDocument document = null;
		
		IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
		findDocument: for(int i = 0; i < windows.length; i++) {
			IWorkbenchPage[] pages = windows[i].getPages();
			for(int j = 0; j < pages.length; j++) {
				IEditorReference[] editors = pages[j].getEditorReferences();
				for(int k = 0; k < editors.length; k++) {
					IEditorPart part = editors[k].getEditor(false);
					if (part != null) {
						if (part instanceof ASLEditor) {
							if (agentFile.equals(((IFileEditorInput)part.getEditorInput()).getFile())) {
								document = ((ASLEditor)part).getDocumentProvider().getDocument(part.getEditorInput());
								break findDocument;
							}
						}
					}
				}
			}
		}
		
		String agentContent = null;
		try {
			InputStream is = agentFile.getContents();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			
			String line;
			StringBuilder content = new StringBuilder();
			
			while ((line = br.readLine()) != null) {
				content.append(line);
				content.append('\n');
			}
			
			agentContent = content.toString();
			Matcher matcher = Pattern.compile("\n[ \\t\\x0B\\f]*\\{\\s*import\\s", Pattern.MULTILINE).matcher(agentContent);
			
			int pos;
			if (matcher.find()) {
				pos = matcher.start() + 1;
			} else {
				matcher = Pattern.compile("^\\s*//").matcher(agentContent);
				if (matcher.find()) {
					pos = agentContent.indexOf("\n", matcher.end()) + 1;
				} else {
					pos = 0;
				}
			}
			
			String localPath = moduleFile.getFullPath().toString();
			String dir = agentFile.getParent().getFullPath().toString();
			if(!dir.endsWith("/") && !dir.endsWith("\\")) dir += "/";
			int dirPos = localPath.toLowerCase().indexOf(dir.toLowerCase());
			if (dirPos != -1) localPath = localPath.substring(dirPos + dir.length());
			
			agentContent = agentContent.substring(0, pos) + ("{import " + moduleName + "=\"" + localPath.replaceAll("\"", "\\\"") + "\"}" + "\n") + agentContent.substring(pos);
		} catch (IOException e) {
			MessageDialog.openError(shell, "Error", e.getMessage());
		}
		
		if (agentContent != null) {
			// agent editor isn't open? no: then modify contents of file, yes: then modify contents of editor.
			if (document == null) {
				agentFile.setContents(new ByteArrayInputStream(agentContent.getBytes()), IFile.KEEP_HISTORY, null);
			} else {
				document.set(agentContent);
			}
		}
		
	}
	
	/**
	 * We will initialize file contents with a sample text.
	 */
	private InputStream openContentStream(IContainer container, String moduleName, String agentSource) {
		try {
			String moduleFileContents = Config.get().getTemplate("module.aslm");
			
			String agentName = agentSource.isEmpty() ? "" : (new Path(agentSource)).lastSegment().replaceFirst("\\.[^.]*$", "");
			String projectName = container.getFile(new Path(agentSource)).getProject().getName();
			
			moduleFileContents = moduleFileContents.replace("<MODULE_NAME>", moduleName);
			moduleFileContents = moduleFileContents.replace("<PROJECT_NAME>", projectName);
			moduleFileContents = moduleFileContents.replace("<AGENT_STRING>", (agentName.isEmpty() ? "" : "of Agent " + agentName + " "));
			moduleFileContents = moduleFileContents.replace("<AG_NAME>", agentName);
			
			return new ByteArrayInputStream(moduleFileContents.getBytes());
		} catch (Exception e) {
			e.printStackTrace();
			MessageDialog.openError(getShell(), "Error", e.getMessage());
			return null;
		}
	}
	
	/**
	 * Throw CoreException for a given message.
	 * 
	 * @param message
	 * @throws CoreException
	 */
	private void throwCoreException(String message) throws CoreException {
		IStatus status = new Status(IStatus.ERROR, Activator.getPluginId(), IStatus.OK, message, null);
		throw new CoreException(status);
	}
	
	/**
	 * We will accept the selection in the workbench to see if we can initialize from it.
	 * 
	 * @see IWorkbenchWizard#init(IWorkbench, IStructuredSelection)
	 */
	public void init(IWorkbench workbench, IStructuredSelection selection) {
		this.selection = selection;
	}
}
